<template>
  <div class="tab-container" @touchstart="handleStart" @touchmove="handleMove" @touchend="handleEnd" @touchcancel="handleEnd">
    <div ref="wrap" :class="{'tab-container-wrap':true, 'tab-container-wrap--animated':animated,}" :style="{ 'transform':`translate3d(${transX}px, 0, 0)`, 'transition-duration':currentTansitionDuration+'ms' }">
      <slot></slot>
    </div>
  </div>
</template>

<script>
import _ from "lodash";
import $ from "jquery";
export default {
  name: "tab-container",
  props: {
    value: {
      default: () => null,
    },
    animateDuration: {
      default: () => 200,
    },
  },
  data() {
    return {
      stepWidth: null,
      eventData: {
        /* 
          currentX,
          currentY,
        */
      },
      transX: 0,
      lastTransX: 0,
      animated: false,
      tabs: [],
    };
  },
  computed: {
    // 实时计算的target
    targetTransX() {
      let width = this.$refs["wrap"].offsetWidth;
      let r;

      r = Math.round(this.transX / width) * width;
      return r;
    },
    currentTansitionDuration() {
      if (this.animated) {
        return this.animateDuration;
      } else {
        return 0;
      }
    },
  },
  watch: {
    value(val, oldVal) {
      if (val === oldVal) {
        return;
      }
      if (val != undefined) {
        this.handleChange(val);
      }
    },
  },
  methods: {
    // 当value 改变时
    handleChange(val) {
      this.animated = true;

      this.initTabs();

      let targetTransX;

      let index = this.tabs.findIndex(tab => {
        return tab.id === val;
      });

      if (index < 0) return;
      let width = this.$refs["wrap"].offsetWidth;
      targetTransX = -width * index;
      this.transX = targetTransX;

      this.animateToTarget(targetTransX);
      this.emit();
    },
    handleStart(e) {
      // 如果不是触摸操作直接return
      if (!e.touches) return;

      this.initTabs();

      if (this.animateDuration <= 0) {
        return;
      }

      if (this.animated) {
        e.preventDefault();
        return;
      }
      this.eventData.isHorizontal = false;
      this.eventData.isTouching = true;
      this.animated = false;
      let t = e.touches[0];

      this.eventData.currentX = this.eventData.startX = t.pageX;
      this.eventData.currentY = this.eventData.startY = t.pageY;

      this.lastTransX = this.transX;
    },
    handleMove(e) {
      if (!e.touches) return;

      if (this.animated) {
        return;
      }
      if (this.animateDuration <= 0) {
        return;
      }
      if (!this.eventData.isTouching) {
        return;
      }

      let t = e.touches[0];

      this.eventData.currentX = t.pageX;
      this.eventData.currentY = t.pageY;

      // 横向滑动
      let startMovingDis = Math.sqrt(
        Math.pow(this.eventData.currentX - this.eventData.startX, 2) +
          Math.pow(this.eventData.currentY - this.eventData.startY, 2)
      );
      if (
        startMovingDis > 10 &&
        startMovingDis < 40 &&
        Math.pow(this.eventData.currentX - this.eventData.startX, 2) /
          Math.pow(this.eventData.currentY - this.eventData.startY, 2) >
          5
      ) {
        this.eventData.isHorizontal = true;
      }
      if (!this.eventData.isHorizontal) {
        return;
      }
      e.preventDefault();
      this.update();
    },
    handleEnd(e) {
      if (this.animateDuration <= 0) {
        return;
      }
      if (!this.eventData.isTouching) return;

      if (this.eventData.isHorizontal) {
        e.preventDefault();
      }

      this.animateToTarget(this.targetTransX).then(() => {
        this.emit();
      });
    },
    animateToTarget(target) {
      return new Promise((resolve, reject) => {
        this.animated = true;
        this.transX = target;
        this.eventData.isTouching = false;
        this.animateDurationTimer = setTimeout(() => {
          this.animated = false;
          this.lastTransX = this.transX;
          this.animateDurationTimer = null;
          resolve();
        }, this.animateDuration);
      });
    },
    getTargetId() {
      let width = this.$refs["wrap"].offsetWidth;
      let index = Math.round(-this.targetTransX / width);
      return this.tabs[index].id;
    },
    emit() {
      this.$emit("input", this.getTargetId());
    },
    update() {
      this.transX =
        this.eventData.currentX - this.eventData.startX + this.lastTransX;
    },
    initTabs() {
      this.tabs = this.$children.map(($VNode, index) => {
        return {
          id: $VNode.$attrs.id,
          index,
          $VNode,
        };
      });
    },
    init() {
      this.initTabs();
    },
  },
  mounted() {
    this.init();
  },
};
</script>
